﻿/*********************************************************************************
 *Author:         OnClick
 *Version:        1.0
 *UnityVersion:   2018.3.11f1
 *Date:           2019-04-16
 *Description:    IFramework
 *History:        2018.11--
*********************************************************************************/
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace IFramework
{
	public static class MethodInfoExtension
	{
        public static void Replace(this MethodInfo self,MethodInfo method)
        {
            if (self.IsVirtual)
                ReplaceVirtualInner(self, method);
            else
                ReplaceStandard(self, method);
        }
        public static void ReplaceStandard(this MethodInfo self, MethodInfo method)
        {
            RuntimeHelpers.PrepareMethod(self.MethodHandle);
            RuntimeHelpers.PrepareMethod(method.MethodHandle);
            IntPtr targetPtr = method.MethodHandle.Value;
            IntPtr replacePtr = self.MethodHandle.Value;
            unsafe
            {
                if (IntPtr.Size == 4)
                {
                    int* inj = (int*)replacePtr.ToPointer() + 2;
                    int* tar = (int*)targetPtr.ToPointer() + 2;
                    if (Debugger.IsAttached)
                    {
                        byte* injInst = (byte*)*inj;
                        byte* tarInst = (byte*)*tar;
                        int* injSrc = (int*)(injInst + 1);
                        int* tarSrc = (int*)(tarInst + 1);
                        *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
                    }
                    else
                    {
                        *tar = *inj;
                    }
                }
                else
                {
                    long* inj = (long*)replacePtr.ToPointer() + 1;
                    long* tar = (long*)targetPtr.ToPointer() + 1;
                    if (Debugger.IsAttached)
                    {
                        byte* injInst = (byte*)*inj;
                        byte* tarInst = (byte*)*tar;
                        long* injSrc = (long*)(injInst + 1);
                        long* tarSrc = (long*)(tarInst + 1);
                        *tarSrc = (((long)injInst + 5) + *injSrc) - ((long)tarInst + 5);
                    }
                    else
                    {
                        *tar = *inj;
                    }
                }
            }
        }
        public static void ReplaceVirtualInner(this MethodInfo self, MethodInfo method)
        {
            RuntimeHelpers.PrepareMethod(self.MethodHandle);
            RuntimeHelpers.PrepareMethod(method.MethodHandle);
            unsafe
            {
                UInt64* methodDesc = (UInt64*)(self.MethodHandle.Value.ToPointer());
                int index = (int)(((*methodDesc) >> 32) & 0xFF);
                if (IntPtr.Size == 4)
                {
                    uint* classStart = (uint*)self.DeclaringType.TypeHandle.Value.ToPointer();
                    classStart += 10;
                    classStart = (uint*)*classStart;
                    uint* tar = classStart + index;
                    uint* inj = (uint*)method.MethodHandle.Value.ToPointer() + 2;
                    if (Debugger.IsAttached)
                    {
                        byte* injInst = (byte*)*inj;
                        byte* tarInst = (byte*)*tar;
                        uint* injSrc = (uint*)(injInst + 1);
                        uint* tarSrc = (uint*)(tarInst + 1);
                        *tarSrc = (((uint)injInst + 5) + *injSrc) - ((uint)tarInst + 5);
                    }
                    else
                    {
                        *tar = *inj;
                    }
                }
                else
                {
                    ulong* classStart = (ulong*)self.DeclaringType.TypeHandle.Value.ToPointer();
                    classStart += 8;
                    classStart = (ulong*)*classStart;
                    ulong* tar = classStart + index;
                    ulong* inj = (ulong*)method.MethodHandle.Value.ToPointer() + 1;
                    if (Debugger.IsAttached)
                    {
                        byte* injInst = (byte*)*inj;
                        byte* tarInst = (byte*)*tar;
                        ulong* injSrc = (ulong*)(injInst + 1);
                        ulong* tarSrc = (ulong*)(tarInst + 1);
                        *tarSrc = (((ulong)injInst + 5) + *injSrc) - ((ulong)tarInst + 5);
                    }
                    else
                    {
                        *tar = *inj;
                    }
                }

            }
        }
    }
}
